package Cammy::Fetch::Weather;
use strict;
use warnings;
# ABSTRACT: Uses MSC's data mart to collect weather data
# VERSION

=head1 SYNOPSIS

    use Cammy::Fetch::Weather;
    my $weather = fetch_weather({
        location_name => "Halifax",
        weather_url => "http://...", # etc
    });

=head1 DESCRIPTION

Uses MSC's data mart to collect weather data. This is pretty
specific to L<Cammy>.

Takes the URL of the citypage used for data.

Weather and warnings will be saved as F<REGION_NAME.weather>
and F<REGION_NAME.warnings>.

=cut

use XML::LibXML;
use File::Stat qw(:stat);
use Config::General qw(ParseConfig);
use Cammy::Util qw(http_client);
use DateTime;
use DateTime::TimeZone;
my $TZ = DateTime::TimeZone->new( name => 'local' );

use Exporter qw(import);
our @EXPORT = qw(report_weather report_sun_data);

=head1 FUNCTIONS

=head2 report_weather

Fetch the weather report. If there is a cache of the report
on-disk, then use it if it is fresh. Otherwise, fetch a new
report, store it, and return the report.

The report is returned as a hashref with keys C<weather> and
C<warnings>, the values of which are strings (or undef).

=cut

sub report_weather {
    my $config = shift;

    my $region = $config->{location_name};
    my $weather_cache  = "$region.weather";
    my $warnings_cache = "$region.warnings";

    if (-f $weather_cache) {
        my $last_modified = time() - stat($weather_cache)->mtime;
        if ($last_modified >= 60*60) { # Older than 1h
            return _fresh_weather(
                url            => $config->{weather_url},
                weather_cache  => $weather_cache,
                warnings_cache => $warnings_cache,
                region         => $region,
            );
        }
        else {
            return _cached_weather(
                url            => $config->{weather_url},
                weather_cache  => $weather_cache,
                warnings_cache => $warnings_cache,
                region         => $region,
            );
        }
    }
    else {
        return _fresh_weather(
            url            => $config->{weather_url},
            weather_cache  => $weather_cache,
            warnings_cache => $warnings_cache,
            region         => $region,
        );
    }
}

sub _cached_weather {
    my %args = @_;
    my $weather_cache  = $args{weather_cache};
    my $warnings_cache = $args{warnings_cache};

    my $weather_report = do {
        open my $in, '<', $weather_cache
            or die "Couldn't open $weather_cache for reading: $!";
        my $text = <$in>;
        chomp $text;
        $text; # "return"
    };

    my $warnings_report = do {
        if (-e $warnings_cache) {
            open my $in, '<', $warnings_cache
                or die "Couldn't open $warnings_cache for reading: $!";
            my $text = <$in>;
            chomp $text;
            $text; # "return"
        }
    };

    return { weather => $weather_report, warnings => $warnings_report };
}

sub _fresh_weather {
    my %args = @_;
    my $url            = $args{url};
    my $weather_cache  = $args{weather_cache};
    my $warnings_cache = $args{warnings_cache};
    my $region         = $args{region};

    my $http = http_client();
    my $xp = do {
        my $http_response = $http->get($url);
        die <<"END_WARN" unless $http_response->{success};
Couldn't download from $http_response->{url}:
HTTP $http_response->{status} $http_response->{reason}
$http_response->{content}
END_WARN

        my $parser = XML::LibXML->new();
        $parser->parse_string( $http_response->{content} ); # "return"
    };
    my $weather_report  = "$region weather as of " . DateTime->now( time_zone => $TZ )->strftime( '%l:%M%P' ) . ': ';

    # Get the condition
    $weather_report .= $xp->find('//currentConditions/condition')->[0]->string_value;

    # Get the temperature
    my $temperature = $xp->find('//currentConditions/temperature')->[0];
    $weather_report .= ' ' . $temperature->string_value . ' deg ' . $temperature->getAttribute('units');

    # Wind data
    $weather_report .= ' Wind: ' . $xp->find('//currentConditions/wind/direction')->[0]->string_value;

    my $speed = $xp->find('//currentConditions/wind/speed')->[0];
    $weather_report .= ' ' . $speed->string_value . ' ' . $speed->getAttribute('units');

    my $gust = $xp->find('//currentConditions/wind/gust')->[0];
    $weather_report .= ' gusting ' . $gust->string_value . ' ' . $gust->getAttribute('units')
        if $gust->string_value;

    # Write to files
    open my $weather, '>', $weather_cache
        or die "Couldn't open $weather_cache for writing: $!";
    print $weather $weather_report, "\n";
    close $weather;

    my $warnings = $xp->find('//warnings/event[@type="warning"]')->[0];
    my $warnings_report;
    if ($warnings) {
        $warnings_report = $warnings->getAttribute('description');
        open my $warnings_out, '>', $warnings_cache
            or die "Couldn't open $warnings_cache for writing: $!";
        print $warnings_out $warnings_report, "\n";
        close $warnings_out;
    }
    else {
        if (-e $warnings_cache) {
            unlink $warnings_cache
                or die "Couldn't unlink $warnings_cache: $!";
        }
    }

    return { weather => $weather_report, warnings => $warnings_report };
}

=head2 report_sun_data

Fetch the sun data (sunrise/sunset) report. If there is a
cache of the report on-disk, then use it if it is fresh.
Otherwise, fetch a new report, store it, and return the report.

The report is returned as a hashref with keys C<sunrise> and
C<sunset>, the values of which are L<DateTime> objects.

=cut

sub report_sun_data {
    my $config = shift;

    my $region = $config->{location_name};
    my $sun_data_cache = "$region.sun_data";

    if (-f $sun_data_cache) {
        my $last_modified = time() - stat($sun_data_cache)->mtime;
        if ($last_modified >= 60*60*24) { # older than 1d
            return _fresh_sun_data(
                url            => $config->{weather_url},
                sun_data_cache => $sun_data_cache,
                region         => $region,
            );
        }
        else {
            return _cached_sun_data(
                url             => $config->{weather_url},
                sun_data_cache  => $sun_data_cache,
                region          => $region,
            );
        }
    }
    else {
        return _fresh_sun_data(
            url            => $config->{weather_url},
            sun_data_cache => $sun_data_cache,
            region         => $region,
        );
    }
}

sub _fresh_sun_data {
    my %args            = @_;
    my $sun_data_cache  = $args{sun_data_cache};
    my $url             = $args{url};

    my $http = http_client();
    my $xp = do {
        my $http_response = $http->get($url);
        die <<"END_WARN" unless $http_response->{success};
Couldn't download from $http_response->{url}:
HTTP $http_response->{status} $http_response->{reason}
$http_response->{content}
END_WARN

        my $parser = XML::LibXML->new();
        $parser->parse_string( $http_response->{content} ); # "return"
    };

    my $rise_xml_timestamp = $xp->find('//riseSet/dateTime[@name="sunrise" and @zone="ADT"]/timeStamp')->[0]->string_value;
    my $set_xml_timestamp  = $xp->find('//riseSet/dateTime[@name="sunset" and @zone="ADT"]/timeStamp')->[0]->string_value;

    my $sunrise_timestamp;
    my $sunset_timestamp;
    my $index_date;
    if ( my @matches = ($rise_xml_timestamp =~ m/^\s*(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)\d\d\s*$/) ){
        my ($y, $m, $d, $h, $M) = @matches;
        $sunrise_timestamp = DateTime->new(
            year        => $y,
            month       => $m,
            day         => $d,
            hour        => $h,
            minute      => $M,
            time_zone   => $TZ
        )->epoch;
        $index_date = $m.$d.$y;
    }
    if ( my @matches = ($set_xml_timestamp =~ m/^\s*(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)\d\d\s*$/) ){
        my ($y, $m, $d, $h, $M) = @matches;
        $sunset_timestamp = DateTime->new(
            year        => $y,
            month       => $m,
            day         => $d,
            hour        => $h,
            minute      => $M,
            time_zone   => $TZ
        )->epoch;
    }

    open my $rise_set_fh, '>', $sun_data_cache or die "Couldn't open $sun_data_cache for writing: $!";
    print $rise_set_fh "$index_date $sunrise_timestamp $sunset_timestamp\n";
    close $rise_set_fh;

    return { sunrise => $sunrise_timestamp, sunset => $sunset_timestamp };
}

sub _cached_sun_data {
    my %args = @_;
    my $sun_data_cache  = $args{sun_data_cache};

    open my $sun_data_in, '<', $sun_data_cache
        or die "Couldn't open $sun_data_cache for reading: $!";
    while (<$sun_data_in>) {
        chomp;
        my ($date, $sunrise, $sunset) = split;
        my $today = DateTime->today( time_zone => $TZ );
        if ( $date eq $today->mdy('') ) { # we found the data for today - return it
            my $sunrise_timestamp = DateTime->from_epoch( epoch => $sunrise, time_zone => $TZ );
            my $sunset_timestamp  = DateTime->from_epoch( epoch => $sunset,  time_zone => $TZ );
            return { sunrise => $sunrise_timestamp, sunset => $sunset_timestamp };
        }
    }
    # search completed unsuccessfully - fetch new data
    return _fresh_sun_data(@_);
}

1;
